package paper.image;

import java.awt.image.*;
import java.io.*;
import java.awt.geom.*;
import java.awt.Rectangle;
//import java.util.Date;
//import java.awt.font.TextLayout;
import java.util.Enumeration;
import java.util.Vector;
import javax.imageio.*;

import java.io.IOException;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileInputStream;
import java.io.BufferedInputStream;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.io.File;
import java.io.PrintWriter;

import java.io.File;
import java.io.InputStream;
import java.lang.Process;
import java.lang.Runtime;


public class ImageHold
{

    private static java.awt.image.BufferedImage image;
    private static java.awt.image.BufferedImage probablyTitle;
    private static File inFile;
    private static int iw;
    private static int ih;
    private static int rWidth;
    private static int rHeight;
    private static int rMinX;
    private static int rMinY;
    private static int rMaxX;
    private static int rMaxY;
	private static Raster raster;
	private static WritableRaster writableRaster;
	// TODO: Work out what a sensible threshold actually *is*, or use something more robust!
	private static double thresholdGrey=0.01;
	private static Vector areaVector;
	private static Vector v;
	private static Vector debugv;
	private static int leftMargin;
	private static int rightMargin;


	public ImageHold(){

	}

	
	public static int getImgWidth(){
		return image.getWidth();
	}
	public static int getImgHeight(){
		return image.getHeight();
	}
	public static BufferedImage getImg(){
		System.out.println("Image is "+image.getWidth()+","+image.getHeight());
		return image;
	}

	public static Raster getRaster(){
		return raster;
	}
	public static Vector getDebugAreas(){
		return debugv;
	}
	
	public static Vector getTempAreas(){
		return v;
	}
	
	public static Vector getAreas(){
		return areaVector;
	}
	
	public static int getLeftMargin(){
		return leftMargin;
	}

	public static int getRightMargin(){
		return rightMargin;
	}

  	public static void generateImg(File outfile) {
        Process child = null;
        Runtime rt;
        StringBuffer sb = null;
        int i;
        String imageMagick="/usr/bin/convert";
		System.out.println("In ImageHold for "+outfile);

        try{
                File f = new File(imageMagick);
                if(!f.exists()){
                        System.err.println("Could not create image (no "+imageMagick+")");
                        return;
                }
        }catch(Exception e){
                System.err.println(e);
                return;
        }
	try{ 
		if(!outfile.exists()){
			System.err.println("PDF "+outfile+" doesn't exist");
		}
	} catch (Exception e){
		System.err.println(e);
		return;
	}

        try{

                String in=""+outfile.getAbsolutePath()+"[0]";
		
	
                //String in=outfile.getAbsolutePath(); // use this instead for converting the lot
                String out=outfile.getAbsolutePath()+".png"; // .png or .jpg? would prefer png for image analysis
                String[] imgmagik=new String[]{imageMagick,in,out};
                System.err.println("Writing image..."+imageMagick+imgmagik[1]);

                rt = Runtime.getRuntime();
                child=rt.exec(imgmagik);
                try{
                        child.waitFor();
                } catch(InterruptedException er){

                }
                sb = new StringBuffer();
                InputStream resultStream = child.getInputStream();
                while((i = resultStream.read()) != -1){
                        sb.append((char)i);
                }
                System.out.println("Result: "+child.exitValue()+" : "+sb.toString());
        } catch (IOException e){
                System.err.println("Couldn't write image..."+e);
        }

    }

	public static void makeThumbnail(File inFile,File outFile){
		if(inFile.exists()){
			System.out.println("File exists "+inFile);
		} else {
			
		}
		if(openPNG(inFile)>-1){
			// do all the image processing stuff, set the raster, etc
			lossyFindAreas();
        		if(!areaVector.isEmpty()){
                		SegmentID segmentID=new SegmentID(areaVector,leftMargin, rightMargin,rWidth);
			
                 		//Rectangle myTmp=(Rectangle) areaVector.firstElement();
                 		Rectangle myTmp=(Rectangle) segmentID.getProbableTitle();
                 		probablyTitle=image.getSubimage((int)myTmp.getX(),(int)myTmp.getY(),(int)myTmp.getWidth(),(int)myTmp.getHeight());
                 		writePNG(outFile,probablyTitle); 
	            	}
		}
	}


	public static void viewTest(File inFile){
		    if(inFile.exists()){
				  System.out.println("File exists "+inFile);
			} else {
			}
			if(openPNG(inFile)>-1){
			// do all the image processing stuff, set the raster, etc
			     lossyFindAreas();

			}
				
	}

	public static int openPNG(File infile){
		System.out.println("infile; "+infile);
		if(infile.exists()){
			System.out.println("File exists "+infile);
		} else {
			System.out.println("No file "+infile);
			return -1;
		}
		iw=0;
		ih=0;
		try{
		  	
  		  	image=ImageIO.read(infile);
			iw = image.getWidth();
            		ih = image.getHeight();
		} catch (IOException e){
			System.err.println("No infile found"+e);
			return -1;
		}
		System.out.println("Image type?"+image.getType()+iw+","+ih);
       		float weight = 1.0f/9.0f;
           float[] elements = new float[9];

           for (int i = 0; i < 9; i++) {
              elements[i] = weight;
           }

           Kernel myKernel = new Kernel(3, 3, elements);
           ConvolveOp simpleBlur = new ConvolveOp(myKernel);

           BufferedImage image2=new BufferedImage(iw,ih,image.getType());
           simpleBlur.filter(image, image2); // blur
	   // problem; this blur mucks up border pixel values
	   // work on unblurred? 
               raster=image2.getData();
               //raster=image.getData();
           rWidth=raster.getWidth();
           rHeight=raster.getHeight();
           rMinX=raster.getMinX();
           rMinY=raster.getMinY();
           rMaxX=rMinX+rWidth; // specs say raster doesn't always start from 0 ..??
           rMaxY=rMinY+rHeight;
	   return 0;

	}

       public static void writePNG(File outfile, BufferedImage img){
                try{
                        ImageIO.write(img, "png", outfile);
                } catch (IOException e){
                        System.err.println("Couldn't write image "+e);

                }
        }

    public static int findLeftVerticalMargin(int expectedLeftMargin, double[] vLine,int vLength){
           int i=0;
           if(vLine[expectedLeftMargin]/vLength< 255-thresholdGrey){
               while((vLine[expectedLeftMargin]/vLength<255-thresholdGrey)&&expectedLeftMargin>0){
                   expectedLeftMargin--;
               }
               expectedLeftMargin++; // rewind
           } else {
               while((vLine[expectedLeftMargin]/vLength>255-thresholdGrey)&&expectedLeftMargin<vLength-1){
                   expectedLeftMargin++;
               }
               expectedLeftMargin--; // rewind
           }
           return expectedLeftMargin;
       }

    public static int findRightVerticalMargin(int expectedRightMargin, double[] vLine,int vLength){
           int i=0;
           if(vLine[expectedRightMargin]/vLength< 255-thresholdGrey){
               while((vLine[expectedRightMargin]/vLength<255-thresholdGrey)&&expectedRightMargin<vLength-1){
                   expectedRightMargin++;
               }
               expectedRightMargin--; //rewind
           } else {
               while((vLine[expectedRightMargin]/vLength>255-thresholdGrey)&&expectedRightMargin>0){
                   expectedRightMargin--;
               }
               expectedRightMargin++; // rewind
           }
           return expectedRightMargin;
       }


    public static Rectangle growVRect(Rectangle rect,Rectangle boundaries){
          int i,j;
          double tempY=rect.getY();
          double tempY1=rect.getY()+rect.getHeight();
          while(getHLineAverage((int)tempY,(int)rect.getX(),(int)rect.getWidth())>(255-thresholdGrey)&&(tempY>boundaries.getY())){
              tempY--;
          }
          while(getHLineAverage((int)tempY1,(int)rect.getX(),(int)rect.getWidth())>255-thresholdGrey&&tempY1<(boundaries.getY()+boundaries.getHeight())){
              tempY1++;
          }
		  if(tempY>rect.getY()){
			tempY++;
		  }
		  if(tempY1<rect.getY()+rect.getHeight()){
			tempY1--;
		  }
          double changeInY=tempY-rect.getY();
          double changeInY1=(rect.getY()+rect.getHeight())-tempY1;
          Rectangle returnVar=new Rectangle((int)rect.getX(),(int)tempY,(int)rect.getWidth(),(int)(rect.getHeight()-(changeInY1+changeInY)));
          return returnVar;
      }
	
    public static Rectangle growRect(Rectangle rect,Rectangle boundaries){
			// grows horiz. rect.
          int i,j;
          double tempX1=rect.getX();
          double tempX=rect.getX()+rect.getWidth();
          while(getVLineAverage((int)tempX,(int)rect.getY(),(int)rect.getHeight())>255-thresholdGrey&&(tempX<boundaries.getX()+boundaries.getWidth())){
              tempX+=2;
          }
	if(tempX>1){
          tempX-=2; // rewind one
			}
          while(getVLineAverage((int)tempX1,(int)rect.getY(),(int)rect.getHeight())>255-thresholdGrey&&(tempX1>boundaries.getX())){
              tempX1-=2;
          }
		  if(tempX<2){
          tempX1+=2; // rewind 1
			}
          double changeInX=tempX1-rect.getX();
          double changeInX1=(rect.getX()+rect.getWidth())-tempX;
          Rectangle returnVar=new Rectangle((int)(tempX1),(int)rect.getY(),(int)((tempX-tempX1)-rect.getWidth()),(int)(rect.getHeight()));
          return returnVar;
      }
	
    public static Rectangle shrinkVRect(Rectangle rect){
          int i,j;
          double tempY=rect.getY();
          double tempY1=rect.getY()+rect.getHeight();
          while(getHLineAverage((int)tempY,(int)rect.getX(),(int)rect.getWidth())>255-thresholdGrey){
              tempY+=1;
          }
	  if(tempY>rect.getY()){
          	tempY-=1;
	  }
          while(getHLineAverage((int)tempY1,(int)rect.getX(),(int)rect.getWidth())>255-thresholdGrey){
              tempY1-=1;
          }
	  if(tempY1<rect.getY()+rect.getHeight()){ 
          tempY1+=1; // rewind 1
	  }

          double changeInY=tempY-rect.getY();
          double changeInY1=(rect.getY()+rect.getHeight())-tempY1;
          Rectangle returnVar=new Rectangle((int)rect.getX(),(int)tempY,(int)(rect.getWidth()),(int)(rect.getHeight()-changeInY-changeInY1));
	  
          return returnVar;
	}
	
    public static Rectangle shrinkRect(Rectangle rect){
          int i,j;
          double tempX=rect.getX();
          double tempX1=rect.getX()+rect.getWidth();
          while(getVLineAverage((int)tempX,(int)rect.getY(),(int)rect.getHeight())>255-thresholdGrey){
              tempX+=2;
          }
	if(tempX>rect.getX()){
          tempX-=2; 
		  }
          while(getVLineAverage((int)tempX1,(int)rect.getY(),(int)rect.getHeight())>255-thresholdGrey){
              tempX1-=2;
          }
	  if(tempX1<rect.getX()+rect.getWidth()){
          tempX1+=2; 
	}
          double changeInX=tempX-rect.getX();
          double changeInX1=(rect.getX()+rect.getWidth())-tempX1;
          Rectangle returnVar=new Rectangle((int)(tempX),(int)rect.getY(),(int)(rect.getWidth()-changeInX-changeInX1),(int)(rect.getHeight()));
          return returnVar;
      }

	public static Rectangle checkForWidthBlanks(Rectangle subdivideMe){
		Rectangle returnVar=new Rectangle(-1,-1,-1,-1); 
		int centreLine=(int)subdivideMe.getX()+((int)subdivideMe.getWidth()/2);
		int mid=centreLine-((int)subdivideMe.getX());
		if (getVLineAverage(centreLine,(int)subdivideMe.getY(),(int)subdivideMe.getHeight())>255-thresholdGrey){
			returnVar=new Rectangle(centreLine,(int)subdivideMe.getY(),1,(int)subdivideMe.getHeight());
			returnVar=growRect(returnVar,subdivideMe);
		} else {
			for(int test=1;test<centreLine-(int)subdivideMe.getX()-8;test++){ 
				if(getVLineAverage(centreLine+test,(int)subdivideMe.getY(),(int)subdivideMe.getHeight())>255-thresholdGrey){
					returnVar=new Rectangle(centreLine+test,(int)subdivideMe.getY(),1,(int)subdivideMe.getHeight());
					returnVar=growRect(returnVar,subdivideMe);
					break;
				} else if(getVLineAverage(centreLine-test,(int)subdivideMe.getY(),(int)subdivideMe.getHeight())>255-thresholdGrey){
					returnVar=new Rectangle(centreLine-test,(int)subdivideMe.getY(),1,(int)subdivideMe.getHeight());
					returnVar=growRect(returnVar,subdivideMe);
					break;
				}
			}
		}
		if(returnVar.getHeight()>1){
			return returnVar; 
		} else {
			return new Rectangle(-1,-1,-1,-1);
		}
	}

	public static Rectangle checkForHeightBlanks(Rectangle subdivideMe){
		Rectangle returnVar=new Rectangle(-1,-1,-1,-1);
		int centreLine=(int)(subdivideMe.getY()+(subdivideMe.getHeight()/2)); 
		
		int countdown=2;
		int countdownb=2;
		if (getHLineAverage(centreLine,(int)subdivideMe.getX(),(int)subdivideMe.getWidth())>255-thresholdGrey){
			returnVar=new Rectangle((int)subdivideMe.getX(),centreLine,(int)subdivideMe.getWidth(),1);
			growVRect(returnVar,subdivideMe);
		} else {
			for(int test=1;test<centreLine-((int)subdivideMe.getY());test++){ // slow but got to be accurate
				if(getHLineAverage(centreLine+test,(int)subdivideMe.getX(),(int)subdivideMe.getWidth())>255-thresholdGrey){
					if(countdown<1){
						returnVar=new Rectangle((int)subdivideMe.getX(),centreLine+test+2,(int)subdivideMe.getWidth(),1);
						returnVar=growVRect(returnVar,subdivideMe);
						break;
					} else {
						countdown--;
					}
				} else {
					countdown=2;
				}
				if(getHLineAverage(centreLine-test,(int)subdivideMe.getX(),(int)subdivideMe.getHeight())>255-thresholdGrey){
					if(countdownb<1){
					returnVar=new Rectangle((int)subdivideMe.getX(),centreLine-test-2,(int)subdivideMe.getWidth(),1);
					returnVar=growVRect(returnVar,subdivideMe);
					break;
					} else {
						countdownb--;
					}
				} else {
					countdownb=2;
				}
			} 
		}
		return returnVar;
	}

	public static double getDensity (Rectangle myRectangle){
		int howmanypixels=(int)(myRectangle.getWidth()*myRectangle.getHeight());
		double pixeltotal=0;
		for(int i=(int)myRectangle.getX();i<(myRectangle.getX()+myRectangle.getWidth());i++){
			for(int j=(int)myRectangle.getY();j<(myRectangle.getY()+myRectangle.getHeight());j++){
				pixeltotal+=pixelValue(i,j);
			}
		}
		return pixeltotal/howmanypixels;
						
	}
	

	public static void subdivideVRectangle(Rectangle subdivideMe, Vector foundRectangles, int maxdepth){
		 if(maxdepth<1){

			foundRectangles.addElement(shrinkVRect(shrinkRect(subdivideMe)));
			return;
		 }		
		 Rectangle myTemp=checkForWidthBlanks(subdivideMe);
		 if(myTemp.getWidth()!=-1&&myTemp.getHeight()!=-1){
		  	//debugv.addElement(myTemp2);
			 int tmpx=(int)myTemp.getX();
			 int tmpx1=(int)(myTemp.getX()+myTemp.getWidth());
		 	Rectangle wTest1=new Rectangle((int)subdivideMe.getX(),(int)subdivideMe.getY(),(int)(myTemp.getX()-subdivideMe.getX()),(int)subdivideMe.getHeight());
			Rectangle wTest2=new Rectangle((int)(myTemp.getX()+myTemp.getWidth()),(int)subdivideMe.getY(),(int)((subdivideMe.getWidth())-(wTest1.getWidth()+myTemp.getWidth())),(int)subdivideMe.getHeight());
			if(wTest1.getWidth()>2 && getDensity(wTest1)<255){  
				subdivideRectangle(wTest1,foundRectangles,maxdepth--);
			}

			if(wTest2.getWidth()>2 && getDensity(wTest2)<255){
				subdivideRectangle(wTest2,foundRectangles,maxdepth--);
			}
		 }  else {
			// well, couldn't subdivide it further in terms of width- bung it in the list
			//foundRectangles.addElement(subdivideMe);
			maxdepth--;
			subdivideRectangle(subdivideMe,foundRectangles,maxdepth);
		}
		 

	}

	 public static void subdivideRectangle(Rectangle subdivideMe, Vector foundRectangles,int maxdepth){
		 if(maxdepth<1){
			foundRectangles.addElement(shrinkVRect(shrinkRect(subdivideMe)));
			return;
		 }		
		 Rectangle myTemp2=checkForHeightBlanks(subdivideMe);
		 if(myTemp2.getWidth()!=-1&&myTemp2.getHeight()!=-1){
		 	Rectangle hTest1=new Rectangle((int)subdivideMe.getX(),(int)subdivideMe.getY(),(int)(subdivideMe.getWidth()),(int)(myTemp2.getY()-subdivideMe.getY()));
			if(hTest1.getHeight()<subdivideMe.getHeight()){
				Rectangle hTest2=new Rectangle((int)subdivideMe.getX(),(int)(myTemp2.getY()+myTemp2.getHeight()),(int)subdivideMe.getWidth(),(int)(subdivideMe.getHeight()-(hTest1.getHeight()+myTemp2.getHeight())));
		   		if(hTest2.getHeight()>4 && getDensity(hTest2)<255){ // bit of a random number
					maxdepth--;
					subdivideRectangle(hTest2,foundRectangles,maxdepth);
				}
			}
		   	if(hTest1.getHeight()>4 &&  getDensity(hTest1)<255){ // bit of a random number
				maxdepth--;
				subdivideRectangle(hTest1,foundRectangles,maxdepth);
			}
		} else {
			maxdepth--;
			subdivideVRectangle(subdivideMe, foundRectangles, maxdepth);
		} 
		 
		 
	}
	 
   public static double getHLineAverage(int j,int initiali, int width){
	double tmp=0;  
	for(int i=initiali;i<(initiali+width);i++){   
		tmp+=pixelValue(i,j);
	}          
	tmp=tmp/(width);   
	return tmp;    
   }

   public static double getVLineAverage(int i,int initialj, int height){
	double tmp=0;  
	for(int j=initialj;j<initialj+height;j++){   
		tmp+=pixelValue(i,j);
	}          
	tmp=tmp/(height);   
	return tmp;    
	}

    public static void lossyFindAreas(){
           // five minute attempt at document segmentation
           int hOffset=2;
           int wOffset=4;
           int blockSize=16;

           double[] hLine=new double[rHeight/hOffset]; // contains averaged total each line
           double[] vLine=new double[rWidth/wOffset]; // contains average total each column
           int expectedLeftMargin=(rWidth/wOffset)/8; // guess at margin location
           int expectedRightMargin=(rWidth/wOffset)-expectedLeftMargin; // guess at right margin location

           double[][] blocks=new double[(rWidth/blockSize)+1][(rHeight/blockSize)+1]; // +1:fudge factor
           System.out.println("Rwidth/RHeight"+((rWidth/blockSize)+1)+","+((rHeight/blockSize)+1));

           int blockw=0;
           int blockh=0;

           int i;
           int j;



           for (j=0;j<rHeight/blockSize;j++){
               for(i=0;i<rWidth/blockSize;i++){
                       blocks[i][j]=0;
                   }
               }


           for (j=0; j<rHeight/hOffset;j++){
               blockh=(j*hOffset)/blockSize;
               for(i=0;i<rWidth/wOffset;i++){
                   blockw=(i*wOffset)/blockSize;
                   blocks[blockw][blockh]+=pixelValue(i*wOffset,j*hOffset); // 32 values in each block
                   hLine[j]+=pixelValue(i*wOffset,j*hOffset);
                   vLine[i]+=pixelValue(i*wOffset,j*hOffset);
               }
           }
	   
	
           int lMargin=findLeftVerticalMargin(expectedLeftMargin,vLine,rHeight/hOffset);
           int rMargin=findRightVerticalMargin(expectedRightMargin,vLine,rHeight/hOffset);

	   leftMargin=lMargin*wOffset;
	   rightMargin=rMargin*wOffset;
           areaVector=new Vector<Rectangle>();
           boolean currentlyWhite=true;

	v=new Vector();
	debugv=new Vector();
           int tmpj=0;
           int countdown=0;
           for(j=2;j<rHeight/hOffset-2;j++){ // edge is buggered
               if(hLine[j]/(rWidth/wOffset)<255-thresholdGrey && currentlyWhite){
                   currentlyWhite=false;
                   countdown=4; // fudge factor
                   tmpj=j;

               }
               if((hLine[j]/(rWidth/wOffset)>255-thresholdGrey) && !currentlyWhite){
                   if(countdown>0){
                       countdown--;
                   } else {
                   currentlyWhite=true;

                   Rectangle tmpRect=new Rectangle(lMargin*wOffset,tmpj*hOffset-hOffset,(rWidth-(lMargin*wOffset)-(rWidth-(rMargin*wOffset))),   (j-tmpj)*hOffset+hOffset); 
                   Rectangle tmpRect2=shrinkVRect(shrinkRect(tmpRect)); // <= tends to cut too thin

		   // ONLY BOTHER DOING THE SUBDIVIDE THING IF IT's A TWO COLUMNER 
		   // XXX: WARNING, subdivideRectangle is UNTESTED
        	   /// subdivideRectangle(tmpRect2, v,4); // rec horiz subdiv
                   areaVector.addElement(tmpRect2);
                   tmpj=0;
                   }
               }
           }
		   	// TODO: analysis stuff (characterise each bit. see discussion of ground truths etc)
	}

       public static double pixelValue(int x, int y) {

           double[] dArray=new double[256];
               if ((x < rMinX) || (x >= rMaxX)) return -1.0;
               if ((y < rMinY) || (y >= rMaxY)) return -1.0;
		// handle boundary effects :-(
		if(x==rMinX || x==rMaxX-1){
			return 255;
		} else if (y==rMinY || y==rMaxY-1){
			return 255;
		} else {
               		raster.getPixel(x, y,dArray);
		}
               return dArray[0];
           }



}
/* Modeline for ViM {{{
 * vim: set ts=4:
 * vim600: fdm=marker fdl=0 fdc=3:
/* Modeline for ViM {{{
 * vim: set ts=4:
 * vim600: fdm=marker fdl=0 fdc=3:
 * }}} */

